package se.prb.cheesymock;

import java.lang.reflect.Method;

/**
 * This enum defines possible behaviours used by CheesyMock to determinie what
 * the return value should be for a method not implemented in any of the
 * delegate objects. There are several possibilities:
 * <dl>
 * <dt>ZERO</dt>
 * <dd>Return zero, false, the empty string or the character '\u0000' depending
 * on the return value type of the method.</dd>
 * <dt>EXCEPTION</dt>
 * <dd>Don't return anything, but throw UnsupportedOperationException.</dd>
 * <dt>VALUES_FROM_METHOD_NAME</dt>
 * <dd>Depending on the type of the return value of the method, return either
 * the name of the method, the hashCode() of the name, the first character in
 * the name or the value true.</dd>
 * </dl>
 *
 * @author björn.ekryd
 */
public enum CheesyMockDefaultBehaviour {
	ZERO {
		@Override
		Object defaultReturnValue(Method method) {
			Object returnType = method.getReturnType();
			if (Integer.TYPE.equals(returnType)) {
				return Integer.valueOf(0);
			} else if (Boolean.TYPE.equals(returnType)) {
				return Boolean.valueOf(false);
			} else if (Long.TYPE.equals(returnType)) {
				return Long.valueOf(0);
			} else if (Float.TYPE.equals(returnType)) {
				return new Float(0);
			} else if (Double.TYPE.equals(returnType)) {
				return new Double(0);
			} else if (Character.TYPE.equals(returnType)) {
				return Character.valueOf('\u0000');
			} else if (Byte.TYPE.equals(returnType)) {
				return Byte.valueOf((byte) 0);
			} else if (Short.TYPE.equals(returnType)) {
				return Short.valueOf((short) 0);
			} else if (String.class.equals(method.getReturnType())) {
				return null;
			}
			return null;
		}
	},
	EXCEPTION {
		@Override
		Object defaultReturnValue(Method method) {
			throw new UnsupportedOperationException("The method " + method.toString() + " is not defined");
		}
	},
	VALUES_FROM_METHOD_NAME {
		@Override
		Object defaultReturnValue(Method method) {
			final String methodName = method.getName();
			Object returnType = method.getReturnType();
			if (Integer.TYPE.equals(returnType) || Integer.class.equals(returnType)) {
				return Integer.valueOf(methodName.hashCode());
			} else if (Boolean.TYPE.equals(returnType) || Boolean.class.equals(returnType)) {
				return Boolean.valueOf(true);
			} else if (Long.TYPE.equals(returnType) || Long.class.equals(returnType)) {
				return Long.valueOf(methodName.hashCode());
			} else if (Float.TYPE.equals(returnType) || Float.class.equals(returnType)) {
				return Float.valueOf(methodName.hashCode());
			} else if (Double.TYPE.equals(returnType) || Double.class.equals(returnType)) {
				return Double.valueOf(methodName.hashCode());
			} else if (Character.TYPE.equals(returnType) || Character.class.equals(returnType)) {
				return methodName.charAt(0);
			} else if (Byte.TYPE.equals(returnType) || Byte.class.equals(returnType)) {
				return Byte.valueOf((byte) methodName.hashCode());
			} else if (Short.TYPE.equals(returnType) || Short.class.equals(returnType)) {
				return Short.valueOf((short) methodName.hashCode());
			} else if (String.class.equals(method.getReturnType())) {
				return methodName;
			}
			return null;
		}
	};
	/**
	 * Generates a default return value for those cases when we can't return
	 * null.
	 *
	 * @param returnType
	 * @return
	 */
	abstract Object defaultReturnValue(Method method);
}
